#!/usr/bin/env python
##############################################################################
#       
#    Copyright 2010 Kaare Hartvig Jensen
#
#    This file is part of yatii.
#
#    yatii is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    yatii is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with yatii.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import os
import gtk
import time
from xmlobject import XMLFile
import inspect
import shutil
import sys

def main():
    program = yatii()
    gtk.main()

##############################################################################
# Class Yatii
##############################################################################
class yatii:
    
    ##########################################################################    
    # ************************************************************************
    # *
    # * Dialogs and functions called when menu buttons are pressed
    # *
    # ************************************************************************
    ##########################################################################
    
    def grid_toggle_item_pressed(self,widget):
        self.toggle_grid = -1*self.toggle_grid
        self.generate_image()
        self.update_preview_image()
    
    def snap_to_grid_toggle_item_pressed(self,widget):
        self.toggle_snap_to_grid = -1*self.toggle_snap_to_grid
     
    # Close application
    def close_application(self, widget, event, data=None):
        gtk.main_quit()
        return False
    
    def menu_save(self,widget):
        self.obj2xml()
    
    def menu_new_dialog(self,widget):
        ####################
        # Description 
        # ===========
        """ This function open a file selection dialog for starting a new project. """
        # Arguments
        # =========
        #
        # Further details
        # ===============
        # 
        ####################
        fc = gtk.FileChooserDialog(title='New yatii project',
                 action=gtk.FILE_CHOOSER_ACTION_SAVE,
                 buttons=(gtk.STOCK_CANCEL,
                     gtk.RESPONSE_CANCEL,
                     gtk.STOCK_SAVE,
                     gtk.RESPONSE_OK))
        # Set input dialog mime type filters 
        filter = gtk.FileFilter()   
        filter.set_name("xml")
        filter.add_pattern("*.xml")
        fc.add_filter(filter)

        # When OK is clicked
        if fc.run() == gtk.RESPONSE_OK:      
            # Get the file name
            self.xmlfile_filename = fc.get_filename()
            shutil.copy(self.new_filename,self.xmlfile_filename)
            #
            self.load_xmlfile()
            # Load the settings
            self.load_settings()
            # Update the list of objects
            self.update_list_of_objects()
            # Generate the image
            self.generate_image()
            # Show the preview
            self.update_preview_image()
            # Update window title
            self.update_window_title(os.path.basename(self.xmlfile_filename))
            # Close dialog
            fc.destroy()
        else:
            # If something else than ok is clicked, close the dialog.
            fc.destroy()

    def update_window_title(self,text):
        self.window.set_title(self.version +" - "+ text)


    def menu_save_as_dialog(self,widget):
        ####################
        # Description 
        # ===========
        """ This function open a file selection dialog for saving the xml file under a new name. """
        # Arguments
        # =========
        #
        # Further details
        # ===============
        # 
        ####################
        fc = gtk.FileChooserDialog(title='Save as',
                 action=gtk.FILE_CHOOSER_ACTION_SAVE,
                 buttons=(gtk.STOCK_CANCEL,
                     gtk.RESPONSE_CANCEL,
                     gtk.STOCK_SAVE,
                     gtk.RESPONSE_OK))
        # Set input dialog mime type filters 
        filter = gtk.FileFilter()   
        filter.set_name("xml")
        filter.add_pattern("*.xml")
        fc.add_filter(filter)

        # When OK is clicked
        if fc.run() == gtk.RESPONSE_OK:      
            # Get the file name
            self.xmlfile_filename = fc.get_filename()
            
            # Save the xml data
            self.obj2xml()
           
            # Update window title
            self.update_window_title(os.path.basename(self.xmlfile_filename))

            # Destroy the dialog
            fc.destroy()
        else:
            # If something else than ok is clicked, close the dialog.
            fc.destroy()

    def menu_export(self,widget):
        ####################
        # Description 
        # ===========
        """ This function open a file selection dialog for exporting the image. """
        # Arguments
        # =========
        #
        # Further details
        # ===============
        # 
        ####################
        fc = gtk.FileChooserDialog(title='Export image',
                 action=gtk.FILE_CHOOSER_ACTION_SAVE,
                 buttons=(gtk.STOCK_CANCEL,
                     gtk.RESPONSE_CANCEL,
                     gtk.STOCK_SAVE,
                     gtk.RESPONSE_OK))
        # Set input dialog mime type filters 
        filter = gtk.FileFilter()   
        filter.set_name("eps")
        filter.add_pattern("*.eps")
        fc.add_filter(filter)

        filter = gtk.FileFilter()   
        filter.set_name("ps")
        filter.add_pattern("*.ps")
        fc.add_filter(filter)

        filter = gtk.FileFilter()   
        filter.set_name("pdf")
        filter.add_pattern("*.pdf")
        fc.add_filter(filter)

        filter = gtk.FileFilter()   
        filter.set_name("png")
        filter.add_pattern("*.png")
        fc.add_filter(filter)

        filter = gtk.FileFilter()   
        filter.set_name("jpg")
        filter.add_pattern("*.jpg")
        fc.add_filter(filter)
        
        filter = gtk.FileFilter()   
        filter.set_name("gif")
        filter.add_pattern("*.gif")
        fc.add_filter(filter)


        # When OK is clicked
        if fc.run() == gtk.RESPONSE_OK:      
            # Get the file name
            self.export_filename = fc.get_filename()

            # Get the export format
            file_details = os.path.splitext(self.export_filename)
            path = file_details[0]
            extension = file_details[1].replace('.','')
            # Has the user enterd a format directly (e.g. test.pdf)
            if extension is '':
                filter = fc.get_filter()
                self.export_format = filter.get_name()
                self.export_filename = self.export_filename + '.' + self.export_format
            else:
                self.export_format = extension

            # Export the file
            self.export_image()
            
            fc.destroy()
        else:
            # If something else than ok is clicked, close the dialog.
            fc.destroy()
 
    def menu_open_file(self,widget):
        ####################
        # Description 
        # ===========
        """ This function open a file selection dialog. """
        # Arguments
        # =========
        #
        # Further details
        # ===============
        # 
        ####################
        # What does fc stand for?
        fc = gtk.FileChooserDialog(title = "Open yatii file...",
                action = gtk.FILE_CHOOSER_ACTION_OPEN,
                buttons = (gtk.STOCK_CANCEL,
                    gtk.RESPONSE_CANCEL,
                    gtk.STOCK_OPEN,
                    gtk.RESPONSE_OK)
                )
        
        # Set input dialog mime type filters 
        filter = gtk.FileFilter()   
        filter.set_name("yatii files")
        filter.add_pattern("*.xml")
        fc.add_filter(filter)
        
        # All files filter
        filter = gtk.FileFilter()
        filter.set_name("All files")
        filter.add_pattern("*")
        fc.add_filter(filter)
        # When OK is clicked
        if fc.run() == gtk.RESPONSE_OK:      
            # Get the file name
            self.xmlfile_filename = fc.get_filename()
            # Load the xml file
            self.load_xmlfile()
            # Load the settings
            self.load_settings()
            # Update the list of objects
            self.update_list_of_objects()
            # Generate the image
            self.generate_image()
            # Show the preview
            self.update_preview_image()
            # Update window title
            self.update_window_title(os.path.basename(self.xmlfile_filename)) 
            # Close dialog
            fc.destroy()
        else:
            # If something else than ok is clicked, close the dialog.
            fc.destroy()
    
    def menu_add_image_dialog(self,widget):
        ####################
        # Description 
        # ===========
        """ This function open a file selection dialog for adding
        an eps image. """
        # Arguments
        # =========
        #
        # Further details
        # ===============
        # 
        ####################
        # What does fc stand for?
        fc = gtk.FileChooserDialog(title = "Open image file",
                action = gtk.FILE_CHOOSER_ACTION_OPEN,
                buttons = (gtk.STOCK_CANCEL,
                    gtk.RESPONSE_CANCEL,
                    gtk.STOCK_OPEN,
                    gtk.RESPONSE_OK)
                )
        
        # Set input dialog mime type filters 
        filter = gtk.FileFilter()   
        filter.set_name("Image files")
        filter.add_pattern("*.eps")
        filter.add_pattern("*.ps")
        fc.add_filter(filter)
        
        # All files filter
        filter = gtk.FileFilter()
        filter.set_name("All files")
        filter.add_pattern("*")
        fc.add_filter(filter)
        # When OK is clicked
        if fc.run() == gtk.RESPONSE_OK:      
            # Get the file name
            filename = fc.get_filename()
            node = self.yatii_data._addNode("object")
            node.type   = "image"
            node.tex    = filename
            node.x      = 0.5
            node.y      = 0.5
            self.update_list_of_objects()
            fc.destroy()

        else:
            # If something else than ok is clicked, close the dialog.
            fc.destroy()

    def menu_settings_dialog(self,widget):
        ####################
        # Description
        # ===========
        """ Dialog that allows the user to change the image settings."""
        # Arguments
        # =========
        #
        # Further Details
        # ===============
        #
        ####################      
        #base this on a message dialog  
        dialog = gtk.MessageDialog(None,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,gtk.MESSAGE_QUESTION,gtk.BUTTONS_OK_CANCEL,None)  
        dialog.set_markup(r"Image settings")  
        
        #Create the text input fields
        entry_image_width = gtk.Entry() 
        entry_image_height = gtk.Entry() 
        entry_font_size = gtk.Entry() 
        entry_image_scale = gtk.Entry()
        entry_grid_spacing = gtk.Entry()

        #Create horizontal boxes to pack the entry and a label  
        hbox_image_scale = gtk.HBox()  
        hbox_image_scale.pack_start(gtk.Label("Image preview scale in percent (e.g. 100)"), False, 5, 5)  
        hbox_image_scale.pack_end(entry_image_scale)
        
        hbox_image_width = gtk.HBox()  
        hbox_image_width.pack_start(gtk.Label("Image width (e.g. 10cm)"), False, 5, 5)  
        hbox_image_width.pack_end(entry_image_width)

        hbox_image_height = gtk.HBox()  
        hbox_image_height.pack_start(gtk.Label("Image height (e.g. 8cm)"), False, 5, 5)  
        hbox_image_height.pack_end(entry_image_height)
        
        hbox_font_size = gtk.HBox()  
        hbox_font_size.pack_start(gtk.Label("Font size (e.g. 10pt)"), False, 5, 5)  
        hbox_font_size.pack_end(entry_font_size)

        hbox_grid_spacing = gtk.HBox()  
        hbox_grid_spacing.pack_start(gtk.Label("Grid spacing (e.g. 0.05)"), False, 5, 5)  
        hbox_grid_spacing.pack_end(entry_grid_spacing)
 

        # Try to fill in the current settings
        
        entry_image_width.set_text(self.yatii_data.settings.imagewidth)
        entry_image_height.set_text(self.yatii_data.settings.imageheight)
        entry_font_size.set_text(self.yatii_data.settings.fontsize)
        entry_image_scale.set_text(self.yatii_data.settings.scale)
        entry_grid_spacing.set_text(self.yatii_data.settings.gridspacing)

        # Show it  
        dialog.vbox.pack_end(hbox_image_scale, True, True, 0)
        dialog.vbox.pack_end(hbox_image_width, True, True, 0)  
        dialog.vbox.pack_end(hbox_image_height, True, True, 0)  
        dialog.vbox.pack_end(hbox_font_size, True, True, 0)  
        dialog.vbox.pack_end(hbox_grid_spacing, True, True, 0)  
        dialog.show_all()  

        #Run the dialog  
        Response=dialog.run()  
        if Response == gtk.RESPONSE_OK:        
            image_width = entry_image_width.get_text()
            image_height = entry_image_height.get_text()
            font_size = entry_font_size.get_text()
            image_scale = entry_image_scale.get_text()
            grid_spacing = entry_grid_spacing.get_text()
            self.yatii_data.settings.imagewidth = image_width
            self.yatii_data.settings.imageheight = image_height
            self.yatii_data.settings.fontsize = font_size
            self.yatii_data.settings.scale = image_scale
            self.yatii_data.settings.gridspacing = grid_spacing
            self.load_settings()
            self.generate_image()
            self.update_preview_image()
            dialog.destroy()  
        else:
            dialog.destroy()

    def menu_edit_object_dialog(self,widget):
        ####################
        # Description
        # ===========
        """ Dialog that allows the user to change the object properties."""
        # Arguments
        # =========
        #
        # Further Details
        # ===============
        #
        ####################      
        #base this on a message dialog  
        dialog = gtk.MessageDialog(None,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,gtk.MESSAGE_QUESTION,gtk.BUTTONS_OK_CANCEL,None)  
        dialog.set_markup(r"Object properties")  

        # Get list of object properties
        attribute_list = self.get_object_attributes(self.yatii_data.object[self.selected_object].type)
        attribute_holder = [] 

        counter = 0
        for attribute in attribute_list:
            attribute_value = getattr(self.yatii_data.object[self.selected_object],attribute)
            # Generate attribute entry box
            entry_attribute = gtk.Entry()
            # Add it to the holder
            attribute_holder.append(entry_attribute)
            hbox_attribute = gtk.HBox()  
            hbox_attribute.pack_start(gtk.Label(attribute), False, 5, 5)
            hbox_attribute.pack_end(attribute_holder[counter])
            attribute_holder[counter].set_text(attribute_value)
            dialog.vbox.pack_end(hbox_attribute, True, True, 0)  
            counter = counter + 1
        
        # Show the content of the dialog
        dialog.show_all()  

        #Run the dialog  
        Response=dialog.run()  
        if Response == gtk.RESPONSE_OK:        
            counter = 0
            for attribute in attribute_list:
                # Get attribute value
                new_attribute_value = attribute_holder[counter].get_text()
                
                # Set attribute value
                setattr(self.yatii_data.object[self.selected_object],attribute,new_attribute_value)
                counter = counter +1
            
            # Update the preview image
            self.generate_image()
            self.update_preview_image()

            # Update list of objects
            self.update_list_of_objects()
        
            # Close the dialog
            dialog.destroy()  
        
        else:
            # Close the dialog
            dialog.destroy()  

    def menu_add_object_dialog(self,type):
        ####################
        # Description
        # ===========
        """ Dialog that allows the user to add an object to the image."""
        # Arguments
        # =========
        #
        # Further Details
        # ===============
        #
        ####################      
        #base this on a message dialog  
        dialog = gtk.MessageDialog(None,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,gtk.MESSAGE_QUESTION,gtk.BUTTONS_OK_CANCEL,None)  
        dialog.set_markup(r"Add " + type)  

        # Get list of object properties
        attribute_list = self.get_object_attributes(type)
        attribute_holder = [] 

        counter = 0
        for attribute in attribute_list:
            #attribute_value = getattr(self.yatii_data.object[self.selected_object],attribute)
            # Generate attribute entry box
            entry_attribute = gtk.Entry()
            # Add it to the holder
            attribute_holder.append(entry_attribute)
            hbox_attribute = gtk.HBox()  
            hbox_attribute.pack_start(gtk.Label(attribute), False, 5, 5)
            hbox_attribute.pack_end(attribute_holder[counter])
            #attribute_holder[counter].set_text(attribute_value)
            dialog.vbox.pack_end(hbox_attribute, True, True, 0)  
            counter = counter + 1
        
        # Show the content of the dialog
        dialog.show_all()  

        #Run the dialog  
        Response=dialog.run()  
        if Response == gtk.RESPONSE_OK:        
            counter = 0
            # Create new object
            node = self.yatii_data._addNode("object")
            setattr(node,'type',type)
            for attribute in attribute_list:
                # Get attribute value
                attribute_value = attribute_holder[counter].get_text()
                
                # Set attribute value
                setattr(node,attribute,attribute_value)
                counter = counter +1
            
            # Update list of objects
            self.update_list_of_objects()

            # Update the preview image
            self.generate_image()
            self.update_preview_image()

            # Update list of objects
            self.update_list_of_objects()
        
            # Close the dialog
            dialog.destroy()  
        
        else:
            # Close the dialog
            dialog.destroy()  
 

    ##########################################################################    
    # ************************************************************************
    # *
    # * LaTeX related functions
    # *
    # ************************************************************************
    ##########################################################################
    def write_latex_code_to_file(self):
        self.clear_texfile()
        self.preamble()
        self.tikzcode()
        self.postamble()
        self.texfile.close()
    
    def latex_compile(self):
        # Normal mode
        if self.tikz_mode == 0:
            print "Compiling mode 0"
            
            latex_command = "latex -shell-escape -halt-on-error -interaction=batchmode -output-directory " +self.temp_path + " " + self.texfile_filename
            print latex_command
            os.popen(latex_command)
            
            dvips_command = "dvips -E -o " + self.psfile_filename + " " + self.dvifile_filename
            print dvips_command
            os.popen(dvips_command)

            ps2eps_command = "ps2eps -f -C -B -nohires " + self.psfile_filename
            print ps2eps_command
            os.popen(ps2eps_command)

            convert_command = "convert -flatten -density "+self.image_scale + " " + self.epsfile_filename + " " + self.giffile_filename
            print convert_command
            os.popen(convert_command)
            print "Compiling done"
        
        # Advanced mode
        if self.tikz_mode == 1:
            print "Compiling"
            command = "latex -shell-escape "+self.texfile_filename
            print command
            os.popen(command)
            print "Compiling done"


    def clear_texfile(self):
        # Close the file
        self.texfile.close()
        # Open and clear the file
        self.texfile = open(self.texfile_filename, 'w')
        self.texfile.writelines('% generated using yatii-gtk\n')
    
    def preamble(self):
        # Normal mode. For users with standard tikz installation
        if self.tikz_mode == 0:
            preamble = r"""
\documentclass[""" + self.image_font_size + r"""]{article}
\usepackage{tikz}
\begin{document}
\pagestyle{empty}
\begin{tikzpicture}[x=""" + self.image_width_cm + r",y=" + self.image_height_cm +"""]
\draw [fill=white,black!1] (0,0) -- (0,1) -- (1,1) -- (1,0) -- (0,0);
"""
            self.texfile.writelines(preamble)
            if self.toggle_grid == 1: 
                self.tikz_grid()

        # Advanced mode. For users with developer version of tikz and pgfplots.
        if self.tikz_mode == 1:
            preamble = r"""
\documentclass[""" + self.image_font_size + r"""]{article}
\usepackage{tikz}
\usepackage{pgfplots}
\usepgfplotslibrary{external}
\tikzexternalize{"""+self.texfile_filename[0:-4]+r"""}
\tikzset{external/system call={latex -shell-escape -halt-on-error -interaction=batchmode -jobname "\image" "\texsource" ; 
dvips -E -o "\image".ps "\image".dvi ; 
ps2eps -f -B -C "\image".ps ;
convert -density 100 "\image".eps "\image".gif}}
\tikzset{external/force remake}
\begin{document}
\pagestyle{empty}
\begin{tikzpicture}[x=""" + self.image_width_cm + r",y=" + self.image_height_cm +"""]
\draw [fill=white,border=white,thin] (0,0) -- (0,1) -- (1,1) -- (1,0) -- (0,0);
"""
            self.texfile.writelines(preamble)
            if self.toggle_grid == 1: 
                self.tikz_grid()


    def tikz_grid(self):
        tikz_grid = "\draw[help lines,step=" + self.grid_spacing + "] (0,0) grid (1,1);"
        self.texfile.writelines(tikz_grid)
        tikz_grid = "\draw[help lines,step=0.25,color=blue] (0,0) grid (1,1);"
        self.texfile.writelines(tikz_grid)
 
    def postamble(self):
        postamble = r"""
\end{tikzpicture}
\end{document}"""
        self.texfile.writelines(postamble) 
    
    def tikzcode(self):
        tikzcode = "\n"
        for i in range(self.Nobject):
            if self.yatii_data.object[i].type == 'equation' or self.yatii_data.object[i].type == 'text':
                tikzcode = tikzcode +   r"\node [] at ("+str(self.yatii_data.object[i].x) + \
                                        r"," + str(self.yatii_data.object[i].y) + r") {" + \
                                        self.yatii_data.object[i].tex + "};"
                tikzcode += "\n"
           
            if self.yatii_data.object[i].type == 'image':
                tikzcode = tikzcode +   r"\node [] at ("+str(self.yatii_data.object[i].x) + \
                                        r"," + str(self.yatii_data.object[i].y) + r") {\includegraphics{" + \
                                        self.yatii_data.object[i].tex + "}};"
                tikzcode += "\n"
            
            if self.yatii_data.object[i].type == 'line':
                tikzcode = tikzcode +   r"\draw["+self.yatii_data.object[i].tex+ "] ("+str(self.yatii_data.object[i].x1) + \
                                        r"," + str(self.yatii_data.object[i].y1) + r") -- " + \
                                        "("+str(self.yatii_data.object[i].x2) + \
                                        r"," + str(self.yatii_data.object[i].y2) + r");"
                tikzcode += "\n"           
            
            if self.yatii_data.object[i].type == 'rectangle':
                tikzcode = tikzcode +   r"\fill["+self.yatii_data.object[i].tex+ "]" + \
                        "("+str(self.yatii_data.object[i].x1) + r"," + str(self.yatii_data.object[i].y1) + r") " + \
                        "rectangle " + \
                        "("+str(self.yatii_data.object[i].x2) + r"," + str(self.yatii_data.object[i].y2) + r");"
                tikzcode += "\n"
            
            if self.yatii_data.object[i].type == 'circle':
                x1 = 0.0
                x2 = 0.0
                y1 = 0.0
                y2 = 0.0    
                try:
                    x1 = float(self.yatii_data.object[i].x1)
                    x2 = float(self.yatii_data.object[i].x2)
                    y1 = float(self.yatii_data.object[i].y1)
                    y2 = float(self.yatii_data.object[i].y2)
                except:
                    pass
                radius = ((x1-x2)**2.0+(y1-y2)**2.0)**(0.5)
                tikzcode = tikzcode +   r"\draw["+self.yatii_data.object[i].tex+ "]" + \
                        "("+str(self.yatii_data.object[i].x1) + r"," + str(self.yatii_data.object[i].y1) + r") " + \
                        "circle " + \
                        "(" + str(radius) + r");"
 
                tikzcode += "\n"
    
    
    
    
        # Write tikz code to file
        self.texfile.writelines(tikzcode) 
 
 
    ##########################################################################    
    # ************************************************************************
    # *
    # * Loading/Saving data related functions
    # *
    # ************************************************************************
    ##########################################################################
    def load_xmlfile(self):
        self.xml_data = XMLFile(path=self.xmlfile_filename)
        self.yatii_data = self.xml_data.root
        
    def load_settings(self):
        self.image_width_cm = self.yatii_data.settings.imagewidth
        self.image_height_cm = self.yatii_data.settings.imageheight
        self.image_font_size = self.yatii_data.settings.fontsize
        self.image_scale = self.yatii_data.settings.scale
        self.grid_spacing = self.yatii_data.settings.gridspacing

    def obj2xml(self):
        xmlfile = open(self.xmlfile_filename, 'w')
        # header
        xml_data = r"""<?xml version="1.0" encoding="UTF-8"?><yatii_data>""" + '\n'
        # Settings
        xml_data += '<settings imagewidth="' + self.image_width_cm + '" ' + \
                    'fontsize="' + self.image_font_size + '" ' + \
                    'gridspacing="' + self.grid_spacing + '" ' + \
                    'scale="' + self.image_scale + '" ' + \
                    'imageheight ="' + self.image_height_cm + '"/>\n'
        # Objects
        for i in range(self.Nobject):
            object_type = self.yatii_data.object[i].type
            # Objects with just a single set of coordinates:
            if object_type == 'text' or object_type == 'equation' or object_type == 'image':
                xml_data += '<object type="' + self.yatii_data.object[i].type +'" '+ \
                        'tex="' + str(self.yatii_data.object[i].tex) + '" ' + \
                        'x="' + str(self.yatii_data.object[i].x) + '" ' + \
                        'y="' + str(self.yatii_data.object[i].y) + '" ' + \
                        '/>\n'
            elif object_type == 'line' or object_type == 'rectangle' or object_type == 'circle':
                xml_data += '<object type="' + self.yatii_data.object[i].type +'" '+ \
                        'tex="' + self.encode(str(self.yatii_data.object[i].tex)) + '" ' + \
                        'x1="' + str(self.yatii_data.object[i].x1) + '" ' + \
                        'x2="' + str(self.yatii_data.object[i].x2) + '" ' + \
                        'y1="' + str(self.yatii_data.object[i].y1) + '" ' + \
                        'y2="' + str(self.yatii_data.object[i].y2) + '" ' + \
                        '/>\n'


        # Footer
        xml_data += "</yatii_data>"
        
        # Write xml to file
        xmlfile.writelines(xml_data)
        # Close the xml file
        xmlfile.close()

    def encode(self,text):
        text = text.replace("&", "&amp;") # must be first!
        text = text.replace("<", "&lt;")
        text = text.replace(">", "&gt;")
        return text.encode("ascii", "xmlcharrefreplace")
 
    ##########################################################################    
    # ************************************************************************
    # *
    # * Image related functions
    # *
    # ************************************************************************
    ##########################################################################
    def refresh_preview(self,widget):
        # clear content of tex file
        self.clear_texfile()
        # write latex code to file
        self.write_latex_code_to_file()
        # compile 
        self.latex_compile()
        # update preview
        self.update_preview_image()

    
    def generate_image(self):
        self.write_latex_code_to_file()
        self.latex_compile()
        
    def update_preview_image(self):
        # Display image in box3
        self.image.clear()
        self.image.set_from_file(self.preview_filename)
        self.image.set_alignment(0,0)
        self.image.set_padding(0,0)
        self.image.show()
        self.image.realize() 
        # Get image size
        sizes = self.image.allocation
        self.image_width_pixel= sizes.width
        self.image_height_pixel = sizes.height

 
    def get_mouse_coordinates(self,widget,event):
        coordinates = [event.x/self.image_width_pixel, 1-event.y/self.image_width_pixel]
        return coordinates

    def get_image_size(self):
        sizes = self.image.allocation
        self.image_width_pixel= sizes.width
        self.image_height_pixel = sizes.height
 
    def print_mouse_coordinate(self,widget,event):
        # scale x and y coordinates because of bounding box error
        x_scale = 1.0
        y_scale = 1.0

        x_shift = 0.0
        y_shift = 0.0

        # Get image size and mouse coordinates
        self.get_image_size()
        x_mouse = x_scale*(event.x/self.image_width_pixel + x_shift)
        y_mouse = y_scale*((self.image_height_pixel-event.y)/self.image_height_pixel+y_shift)
        
        # Snap to grid
        if self.toggle_snap_to_grid == 1:
            x_mouse = round(x_mouse/float(self.grid_spacing))*float(self.grid_spacing)
            y_mouse = round(y_mouse/float(self.grid_spacing))*float(self.grid_spacing)
        
        # Add mouse coordinates to the list
        self.mouse_coordinates_x.append(x_mouse)
        self.mouse_coordinates_y.append(y_mouse)

        # Update object coordinates
        self.update_object_coordinates()
        
       
    ##########################################################################    
    # ************************************************************************
    # *
    # * Export related functions.
    # *
    # ************************************************************************
    ##########################################################################
    def export_image(self):
        print self.export_format
        if self.export_format == 'eps':
            shutil.copy(self.epsfile_filename,self.export_filename)
        elif self.export_format == 'ps':
            shutil.copy(self.psfile_filename,self.export_filename)
        elif self.export_format == 'pdf':
            self.generate_pdf()
            shutil.copy(self.pdffile_filename,self.export_filename)
        elif self.export_format == 'png':
            self.generate_png()
            shutil.copy(self.pngfile_filename,self.export_filename)
        elif self.export_format == 'jpg':
            self.generate_jpg()
            shutil.copy(self.jpgfile_filename,self.export_filename)
        elif self.export_format == 'gif':
            self.generate_gif()
            shutil.copy(self.giffile_filename,self.export_filename)
 
    def generate_pdf(self):
        command = 'epstopdf ' + self.epsfile_filename
        os.popen(command)
    
    def generate_png(self):
        command = 'convert -density 100 ' + self.epsfile_filename + ' ' + self.pngfile_filename
        os.popen(command)

    def generate_jpg(self):
        command = 'convert -density 100 ' + self.epsfile_filename + ' ' + self.jpgfile_filename
        os.popen(command)

    def generate_gif(self):
        command = 'convert -density 100 ' + self.epsfile_filename + ' ' + self.giffile_filename
        os.popen(command)

    ##########################################################################    
    # ************************************************************************
    # *
    # * Object list related functions. Dialogs for adding e.g. text
    # * has a separate category
    # *
    # ************************************************************************
    ##########################################################################
    # callback - function runs when a checkbutton is toggled
    def update_object_coordinates(self):
        # Get object type
        object_type = self.yatii_data.object[self.selected_object].type
        # Objects with just a single set of coordinates:
        if object_type == 'text' or object_type == 'equation' or object_type == 'image':
            self.yatii_data.object[self.selected_object].x = self.mouse_coordinates_x[-1]
            self.yatii_data.object[self.selected_object].y = self.mouse_coordinates_y[-1]
        # Objects with two coordinates (lines):
        elif object_type == 'line' or object_type == 'rectangle' or object_type == 'circle':
            self.yatii_data.object[self.selected_object].x1 = self.mouse_coordinates_x[-2]
            self.yatii_data.object[self.selected_object].x2 = self.mouse_coordinates_x[-1]
            self.yatii_data.object[self.selected_object].y1 = self.mouse_coordinates_y[-2]
            self.yatii_data.object[self.selected_object].y2 = self.mouse_coordinates_y[-1]

    def callback(self, widget, data=None):
        # The selected
        self.selected_object = data
        text = self.get_object_statusbar_text(self.yatii_data.object[data].type)
        self.statusbar.push(1, text)


    def get_object_attributes(self,type):
        if type == 'text':
            return ['tex','x','y']
        elif type == 'equation':
            return ['tex','x','y']
        elif type == 'image':
            return ['tex','x','y']
        elif type == 'line':
            return ['tex','x1','x2','y1','y2']
        elif type == 'rectangle':
            return ['tex','x1','x2','y1','y2']
        elif type == 'circle':
            return ['tex','x1','x2','y1','y2']
        else:
            return []
    
    def get_object_statusbar_text(self,type):
        if type == 'text':
            return 'Text object. Click the desired location in the image.'
        elif type == 'equation':
            return 'Equation object. Click the desired location in the image.'
        elif type == 'image':
            return 'Image object. Clock the desired location in the image.'
        elif type == 'line':
            return 'Line object. First click: Start point, second click: End point.'
        elif type == 'rectangle':
            return 'Rectangle object. First click: Lower left corner, second click: Upper right corner.'
        elif type == 'circle':
            return 'Circle object. First click: Circle center. Second click: Point on perifery.'
        else:
            return ''


    def update_list_of_objects(self):
        # Box containing list of objects
        try: 
            self.box2.destroy()
        except:
            pass

        self.box2 = gtk.VBox(False, 10)
        self.box2.set_border_width(10)
        self.box2a.pack_start(self.box2, True, True, 0)
        self.box2.show()
        # Create buttons
        # First, dummy group button 
        button = gtk.RadioButton(None, "radio button2")
	    # Add objects to list in main window
        self.Nobject = len(self.yatii_data.object)

        for i in range(self.Nobject):
            button = gtk.RadioButton(button, self.yatii_data.object[i].type + " : " + self.yatii_data.object[i].tex)
#            if self.yatii_data.object[i].type == 'image': button.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("gray")) 
            text = self.get_object_statusbar_text(self.yatii_data.object[i].type)
            button.set_tooltip_text(text)
            button.connect("pressed", self.callback,i)
            self.box2.pack_start(button, True, True, 0)
            button.show()
            if i == self.Nobject-1:
                self.selected_object = i
                button.set_active(gtk.TRUE)
                self.statusbar.push(1, text)

    def move_object_up(self,widget):
        if self.selected_object is not self.Nobject-1:
            self.yatii_data.object[self.selected_object], self.yatii_data.object[self.selected_object+1] = \
                self.yatii_data.object[self.selected_object+1] ,  self.yatii_data.object[self.selected_object]
            # Update list of objects
            self.selected_object = self.selected_object + 1
            self.update_list_of_objects()
    
    def move_object_down(self,widget):
        if self.selected_object is not 0:
            self.yatii_data.object[self.selected_object], self.yatii_data.object[self.selected_object-1] = \
                self.yatii_data.object[self.selected_object-1] ,  self.yatii_data.object[self.selected_object]
            # Update list of objects    
            self.selected_object = self.selected_object - 1
            self.update_list_of_objects()

    def remove_selected(self,widget):
        # remove selected object
        print self.yatii_data.object[self.selected_object].type

        # Delete the selected object from the python object. Unfortunately,
        # this is not propagated back into the xml file. 
        del self.yatii_data.object[self.selected_object]

        self.update_list_of_objects()
        #self.generate_image()
        #self.update_preview_image()
 
## Main init function
    def __init__(self):
        ######################################################################
	    # Golbal variables
       	######################################################################
    	self.settings_attributes = ['scale','imagewidth','imageheight','fontsize']
        self.version = "yatii beta 2.0.0"
        ####
        # path and file names
        ####
        self.temp_path = '/tmp/yatii/'
        # Create temp dir if not already there
        if not os.path.exists(self.temp_path):
            os.makedirs(self.temp_path)
            

        # Terrible, terrible(!) hack to determine sys.prefix correctly
        if os.path.exists('/usr/share/yatii'):
            prefix = '/usr'
        elif os.path.exists('/usr/local/share/yatii'):
            prefix = '/usr/local'

        # Set file names
        self.welcome_gif_filename = prefix+'/share/yatii/welcome.gif'   
        self.new_filename = prefix + '/share/yatii/new.xml'
        
        self.texfile_filename = self.temp_path +'texfile.tex'
        self.dvifile_filename = self.temp_path +'texfile.dvi'
        self.psfile_filename = self.temp_path +'texfile.ps'
        self.epsfile_filename = self.temp_path +'texfile.eps'
        self.giffile_filename = self.temp_path +'texfile.gif'
        self.pngfile_filename = self.temp_path +'texfile.png'
        self.jpgfile_filename = self.temp_path +'texfile.jpg'
        self.pdffile_filename = self.temp_path + 'texfile.pdf'


        self.texfile_basename = 'texfile'
#        self.preview_filename = 'texfile-figure0.gif'
        self.preview_filename = self.giffile_filename

        self.texfile = open(self.texfile_filename, 'w')
        self.texfile.writelines('% generated using yatii-gtk\n')

        
        # Constants
        self.tikz_mode = 0
        self.selected_object = 1
        self.mouse_coordinates_x = [0, 1]
        self.mouse_coordinates_y = [0, 1]

        ######################################################################
        # Create Geometry
       	######################################################################
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("delete_event", self.close_application)
        self.update_window_title('')
        self.window.set_border_width(0)
#        self.window.set_size_request(600, 600)
        self.window.set_position(gtk.WIN_POS_CENTER)

      
        # Keyboard shortcuts
        agr = gtk.AccelGroup()
        self.window.add_accel_group(agr)
      
        # Box containing text strings to put on image
        self.box1 = gtk.VBox(False, 2)
        self.window.add(self.box1)
        #self.box1.show()

        # Menu bar
        menu_bar = gtk.MenuBar()
        self.box1.add(menu_bar)
        #menu_bar.show()
        
        ######################################################################
        # File Menu item
        ######################################################################
        file_item = gtk.MenuItem("File")
        #file_item.show()
        file_menu = gtk.Menu()

        # File menu submenus
        # Create items
        new_item = gtk.MenuItem("New")
        open_item = gtk.MenuItem("Open")
        save_item = gtk.MenuItem("Save")
        save_as_item = gtk.MenuItem("Save as")
        export_item = gtk.MenuItem("Export")
        quit_item = gtk.MenuItem("Quit")

        # Add them to the menu
        file_menu.append(new_item)
        file_menu.append(open_item)
        file_menu.append(save_item)
        file_menu.append(save_as_item)
        file_menu.append(export_item)
        file_menu.append(quit_item)

        # Attach the callback functions to the activate signal of the file menu items
        new_item.connect_object("activate", self.menu_new_dialog, "file.new")
        key, mod = gtk.accelerator_parse("<control>n")
        new_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)
        
        open_item.connect_object("activate", self.menu_open_file, "file.open")
        key, mod = gtk.accelerator_parse("<control>o")
        open_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)

        save_item.connect_object("activate", self.menu_save, "file.save")
        key, mod = gtk.accelerator_parse("<control>s")
        save_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)

        save_as_item.connect_object("activate", self.menu_save_as_dialog, "file.save_as")
        key, mod = gtk.accelerator_parse("<control><shift>s")
        save_as_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)

        export_item.connect_object("activate", self.menu_export, "file.export")

        #save_item.connect_object("activate", menuitem_response, "file.save")
        quit_item.connect_object ("activate", self.close_application,self.window, "file.quit")
        key, mod = gtk.accelerator_parse("<control>q")
        quit_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)

        # Show file menu items
        #new_item.show()
        #open_item.show()
        #save_item.show()
        #quit_item.show()
        file_item.set_submenu(file_menu)
        menu_bar.append(file_item)
        

        ######################################################################
        # "Edit" Menu item
        ######################################################################
        edit_item = gtk.MenuItem("Edit")
        #edit_item.show()
        edit_menu = gtk.Menu()
        # Add menu submenus
        # Create items
        settings_item = gtk.MenuItem("Settings",agr)
        key, mod = gtk.accelerator_parse("<control>p")
        settings_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)
        
        object_item = gtk.MenuItem("Object Properties",agr)
        key, mod = gtk.accelerator_parse("<control>e")
        object_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)

        remove_selected_item = gtk.MenuItem("Remove selected",agr)
        key, mod = gtk.accelerator_parse("<control>d")
        remove_selected_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)
        
        move_object_up_item = gtk.MenuItem("Move object up",agr)
        key, mod = gtk.accelerator_parse("plus")
        move_object_up_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)
        
        move_object_down_item = gtk.MenuItem("Move object down",agr)
        key, mod = gtk.accelerator_parse("minus")
        move_object_down_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)


        # Add them to the menu
        edit_menu.append(settings_item)
        edit_menu.append(object_item)
        edit_menu.append(remove_selected_item)
        edit_menu.append(move_object_up_item)
        edit_menu.append(move_object_down_item)
        
        # Attach the callback functions to the activate signal of the file menu items
        settings_item.connect_object("activate", self.menu_settings_dialog, "edit.settings")
        object_item.connect_object ("activate", self.menu_edit_object_dialog, "edit.object")
        remove_selected_item.connect_object ("activate", self.remove_selected, "edit.remove_selected")
        move_object_up_item.connect_object ("activate", self.move_object_up, "edit.move_object_up")
        move_object_down_item.connect_object ("activate", self.move_object_down, "edit.move_object_down")
       
        # Show add menu items
        edit_item.set_submenu(edit_menu)
        menu_bar.append(edit_item)
     
        ######################################################################
        # "Add" Menu item
        ######################################################################
        add_item = gtk.MenuItem("Add")
        add_item.show()
        add_menu = gtk.Menu()
        # Add menu submenus
        # Create items
        text_item = gtk.MenuItem("Text")
        equation_item = gtk.MenuItem("Equation")
        image_item = gtk.MenuItem("Image")
        line_item = gtk.MenuItem("Line")
        rect_item = gtk.MenuItem("Rectangle")
        circle_item = gtk.MenuItem("Circle")

        # Add them to the menu
        add_menu.append(text_item)
        add_menu.append(equation_item)
        add_menu.append(image_item)
        add_menu.append(line_item)
        add_menu.append(rect_item)
        add_menu.append(circle_item)

        # Attach the callback functions to the activate signal of the file menu items
        text_item.connect_object ("activate", self.menu_add_object_dialog, "text")
        key, mod = gtk.accelerator_parse("F1")
        text_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)

        equation_item.connect_object ("activate", self.menu_add_object_dialog, "equation")
        key, mod = gtk.accelerator_parse("F2")
        equation_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)

        image_item.connect_object ("activate", self.menu_add_image_dialog, "image")
        key, mod = gtk.accelerator_parse("F3")
        image_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)

        line_item.connect_object ("activate", self.menu_add_object_dialog, "line")
        key, mod = gtk.accelerator_parse("F4")
        line_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)
        
        rect_item.connect_object ("activate", self.menu_add_object_dialog, "rectangle")
        key, mod = gtk.accelerator_parse("F5")
        rect_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)


        circle_item.connect_object ("activate", self.menu_add_object_dialog, "circle")
        key, mod = gtk.accelerator_parse("F6")
        circle_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)

        add_item.set_submenu(add_menu)
        menu_bar.append(add_item)
        #edit_menu.append(add_item)
        
        ######################################################################
        # "View" Menu item
        ######################################################################
        view_item = gtk.MenuItem("View")
        view_item.show()
        view_menu = gtk.Menu()
        view_item.set_submenu(view_menu)
        menu_bar.append(view_item)

        grid_toggle_item = gtk.CheckMenuItem("Toggle grid")
        grid_toggle_item.set_active(True)
        self.toggle_grid = 1
        
        snap_to_grid_toggle_item = gtk.CheckMenuItem("Toggle snap to grid")
        snap_to_grid_toggle_item.set_active(True)
        self.toggle_snap_to_grid = 1
 
        refresh_item = gtk.MenuItem("Refresh preview",agr)
        key, mod = gtk.accelerator_parse("space")
        refresh_item.add_accelerator("activate",agr,key,mod,gtk.ACCEL_VISIBLE)

        view_menu.append(grid_toggle_item)
        view_menu.append(snap_to_grid_toggle_item)
        view_menu.append(refresh_item)
        
        grid_toggle_item.connect("toggled", self.grid_toggle_item_pressed)
        snap_to_grid_toggle_item.connect("toggled", self.snap_to_grid_toggle_item_pressed)
        refresh_item.connect_object ("activate", self.refresh_preview, "edit.refresh_preview")
 

        ######################################################################
        # "Help" Menu item
        ######################################################################
        help_item = gtk.MenuItem("Help")
        help_item.show()
        help_menu = gtk.Menu()
        help_item.set_submenu(help_menu)
        menu_bar.append(help_item)
        
        about_item = gtk.MenuItem("About")
        help_menu.append(about_item)


        # Box containing image
        self.box3 = gtk.EventBox()
        self.box1.pack_start(self.box3,True,True,0)

        # Catch mouse clicks on image
        self.box3.set_events(gtk.gdk.BUTTON_PRESS_MASK)
        self.box3.connect("button_press_event", self.print_mouse_coordinate)

        # Initialise preview image in box3
        self.image = gtk.Image()
        self.image.set_from_file(self.welcome_gif_filename)
        self.image.set_alignment(0,0)
        self.image.set_padding(0,0)

        self.box3.add(self.image)

        #self.image.show()
        self.image.realize() 


        self.box2a = gtk.VBox(False, 0)
        self.box2a.set_border_width(0)
        self.box1.pack_start(self.box2a, True, True, 0)
        self.box2a.show()
 
        self.statusbar = gtk.Statusbar()
        self.statusbar.push(1, "Ready")
        self.box1.pack_start(self.statusbar, False, False, 0)

        # Show the main window
        self.window.show_all()
        self.window.set_resizable(False)
        self.window.add_events(gtk.gdk.BUTTON_PRESS_MASK)
#        self.window.set_cursor(gtk.gdk.CROSSHAIR)
#def main():
#    gtk.main()
#    return 0        

#if __name__ == "__main__":
#    Yatii()
#    main()

